﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

namespace UnityView
{
    public interface IUnityCanvas
    {
        public Canvas Canvas { get; }
        public CanvasScaler CanvasScaler { get; }
        public GraphicRaycaster GraphicRaycaster { get; }
    }

    public class UICanvas : IViewLayout, IUnityCanvas
    {
        private static UICanvas _instance;

        static UICanvas()
        {
            SceneManager.activeSceneChanged += OnSceneChanged;
        }

        public static void OnSceneChanged(Scene from, Scene to)
        {
            _instance = new UICanvas();
        }

        public Canvas Canvas { get; protected set; }
        public CanvasScaler CanvasScaler { get; protected set; }
        public GraphicRaycaster GraphicRaycaster { get; protected set; }

        public readonly EventSystem EventSystem;
        public readonly StandaloneInputModule InputModule;

        public static UICanvas GetInstance()
        {
            if (_instance != null) return _instance;
            _instance = new UICanvas();
            return _instance;
        }

        public float Width => CanvasScaler.referenceResolution.x;

        public float Height => CanvasScaler.referenceResolution.y;

        public RectTransform RectTransform { get; protected set; }

        public readonly GameObject UIObject;

        public static Vector3 CanvasScale = Vector3.one;

        private UICanvas()
        {
            Canvas = Object.FindObjectOfType<Canvas>();
            if (Canvas == null)
            {
                UIObject = new GameObject();
                UIObject.name = "UI Canvas";
                UIObject.layer = UIConstant.LayerDefault;

                RectTransform = UIObject.AddComponent<RectTransform>();
                Canvas = UIObject.AddComponent<Canvas>();
            }
            else
            {
                UIObject = Canvas.gameObject;
                RectTransform = UIObject.GetComponent<RectTransform>() ?? UIObject.AddComponent<RectTransform>();
            }
            Canvas.renderMode = RenderMode.ScreenSpaceOverlay;
            CanvasScaler = UIObject.GetComponent<CanvasScaler>();
            if (CanvasScaler == null)
            {
                CanvasScaler = UIObject.AddComponent<CanvasScaler>();
                CanvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
                CanvasScaler.referenceResolution = new Vector2(UIConstant.DesignWidth, UIConstant.DesignHeight);
                CanvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
                CanvasScaler.matchWidthOrHeight = 0;
                CanvasScaler.referencePixelsPerUnit = 100;
            }
            GraphicRaycaster = UIObject.GetComponent<GraphicRaycaster>() ?? UIObject.AddComponent<GraphicRaycaster>();

            UIViewManager.Instance = UIObject.GetComponent<UIViewManager>() ?? UIObject.AddComponent<UIViewManager>();
            CanvasScale = RectTransform.localScale;

            EventSystem = Object.FindObjectOfType<EventSystem>();
            if (EventSystem == null)
            {
                EventSystem = UIObject.AddComponent<EventSystem>();
                EventSystem.firstSelectedGameObject = UIObject;
                EventSystem.sendNavigationEvents = false;
            }

            InputModule = Object.FindObjectOfType<StandaloneInputModule>();
            if (InputModule == null)
            {
                InputModule = UIObject.AddComponent<StandaloneInputModule>();
            }
        }

        public IViewLayout ParentView
        {
            get => null;
            set => Debug.LogWarning(
                $"UI Canvas should not have any parent, want to set {value.RectTransform.gameObject.name}");
        }

        protected List<IViewLayout> Subviews = new List<IViewLayout>();

        public void AddSubview(IViewLayout view)
        {
            view.ParentView = this;
            Subviews.Add(view);
        }

        public void RemoveSubview(IViewLayout view)
        {
            view.ParentView = null;
            Subviews.Remove(view);
        }

        public void Destroy()
        {
            GameObject.Destroy(UIObject);
        }

        public RenderMode RenderMode => Canvas.renderMode;

        public bool Enable
        {
            set => UIObject.SetActive(value);
            get => UIObject.activeSelf;
        }

        public static bool InCanvas(Vector2 point)
        {
            var canvas = GetInstance();
            if (canvas.RenderMode == RenderMode.ScreenSpaceOverlay)
            {
                return point.x > 0 && point.x < canvas.Width && point.y > 0 && point.y < canvas.Height;
            }
            if (canvas.RenderMode == RenderMode.WorldSpace)
            {
                var rectTransform = canvas.RectTransform;
                float left = rectTransform.anchoredPosition3D.x;
                float top = rectTransform.anchoredPosition3D.y;
                float right = left + rectTransform.sizeDelta.x;
                float bottom = top + rectTransform.sizeDelta.y;
                return point.x > left && point.x < right && point.y > top && point.y < bottom;
            }
            return false;
        }

        public static Rect UIFrameToScreenRect(UIFrame frame)
        {
            return new Rect(frame.x * UIConstant.ScalingWidth, frame.y * UIConstant.ScalingHeight,
                frame.width * UIConstant.ScalingWidth, frame.height * UIConstant.ScalingHeight);
        }

        public static Vector2 MousePosition()
        {
            Vector2 p = Input.mousePosition;
            p = new Vector2(p.x / UIConstant.ScalingWidth, p.y / UIConstant.ScalingHeight);
            return new Vector2(p.x, UIConstant.DesignHeight - p.y);
        }
    }
}